package com.uuah.server.component.model.resolvers;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.uuah.server.api.IUuahEventContext;
import com.uuah.server.api.component.model.IEntryPointResolver;
import com.uuah.server.component.VoidResult;
import com.uuah.server.component.model.InvocationResult;
import com.uuah.server.exception.transformer.TransformerException;
import com.uuah.server.transport.NullPayload;
import com.uuah.utils.ClassUtils;
import com.uuah.utils.StringUtils;

/**
 * A Base class for
 * {@link com.uuah.server.api.component.model.IEntryPointResolver}. It provides
 * parameters for detemining if the payload of the message should be transformed
 * first and whether void methods are acceptible. It also provides a method
 * cashe for those resolvers that use reflection to discover methods on the
 * service.
 *
 * <p>
 * update record:updator，update time，update content and version
 * </p>
 *
 * @author <a href="jonny_quan@hotmail.com">jonny</a>
 * @date 2009 2009-3-27 上午09:34:20
 * @version 1.0.0
 */
public abstract class AbstractEntryPointResolver implements IEntryPointResolver {
	protected transient final Logger logger = LoggerFactory
			.getLogger(getClass());

	private boolean transformFirst = true;

	private boolean acceptVoidMethods = false;

	protected final ConcurrentHashMap methodCache = new ConcurrentHashMap(4);

	public boolean isTransformFirst() {
		return transformFirst;
	}

	public void setTransformFirst(boolean transformFirst) {
		this.transformFirst = transformFirst;
	}

	public boolean isAcceptVoidMethods() {
		return acceptVoidMethods;
	}

	public void setAcceptVoidMethods(boolean acceptVoidMethods) {
		this.acceptVoidMethods = acceptVoidMethods;
	}

	protected Method getMethodByName(String methodName, IUuahEventContext context) {
		StringBuffer key = new StringBuffer(24).append(
				context.getService().getName()).append(".").append(methodName);
		Method method = (Method) methodCache.get(key.toString());
		return method;
	}

	protected Method addMethodByName(Method method, IUuahEventContext context) {
		StringBuffer key = new StringBuffer(24).append(
				context.getService().getName()).append(".").append(
				method.getName());
		Method previousMethod = (Method) methodCache.putIfAbsent(
				key.toString(), method);
		return (previousMethod != null ? previousMethod : method);
	}

	protected String getCacheKeyForPayload(Object component, Object[] payload) {
		StringBuffer key = new StringBuffer(48);
		for (int i = 0; i < payload.length; i++) {
			Object o = payload[i];
			if (o != null) {
				key.append(o.getClass().getName());
			} else {
				key.append("null");
			}
		}
		key.append(".").append(ClassUtils.getClassName(component.getClass()));
		return key.toString();
	}

	protected Object[] getPayloadFromMessage(IUuahEventContext context)
			throws TransformerException {
		Object temp;
		if (isTransformFirst()) {
			temp = context.transformMessage();
		} else {
			temp = context.getMessage().getPayload();
		}
		if (temp instanceof Object[]) {
			return (Object[]) temp;
		} else if (temp instanceof NullPayload) {
			return ClassUtils.NO_ARGS;
		} else {
			return new Object[] { temp };
		}
	}

	protected InvocationResult invokeMethod(Object component, Method method,
			Object[] arguments) throws InvocationTargetException,
			IllegalAccessException {
		String methodCall = null;

		if (logger.isDebugEnabled()) {
			methodCall = component.getClass().getName() + "."
					+ method.getName() + "("
					+ StringUtils.toString(ClassUtils.getClassTypes(arguments))
					+ ")";
			logger.debug("Invoking " + methodCall);
		}

		Object result = method.invoke(component, arguments);
		if (method.getReturnType().equals(Void.TYPE)) {
			result = VoidResult.getInstance();
		}

		if (logger.isDebugEnabled()) {
			logger.debug("Result of call " + methodCall + " is "
					+ (result == null ? "null" : "not null"));
		}

		return new InvocationResult(result, method);
	}

	public String toString() {
		final StringBuffer sb = new StringBuffer();
		sb.append("AbstractEntryPointResolver");
		sb.append("{transformFirst=").append(transformFirst);
		sb.append(", acceptVoidMethods=").append(acceptVoidMethods);
		sb.append('}');
		return sb.toString();
	}
}
